Skip to content

fix(ci,windows): gate release-aggregate on real evidence; win32 drain no longer double-reads - #109

Merged
stuinfla merged 6 commits into
mainfrom
fix/4.0.8-release-process
Aug 6, 2026
Merged

fix(ci,windows): gate release-aggregate on real evidence; win32 drain no longer double-reads#109
stuinfla merged 6 commits into
mainfrom
fix/4.0.8-release-process

Conversation

@stuinfla

@stuinfla stuinfla commented Aug 3, 2026

Copy link
Copy Markdown
Owner
  • fix(hooks): bound Codex stdin reads
  • fix(release): make 4.0.8 publication resumable
  • docs(adr): reconcile 4.0.8 release surfaces
  • fix(replay): preflight isolated Codex hooks
  • fix(replay): normalize Codex command envelopes
  • fix(replay): parse global Ruflo shell paths
  • test(replay): bind current D4 evidence
  • fix(release): prove Codex payloads and bound Windows stdin
  • docs(adr): refresh 95 contract currency
  • fix(release): validate Codex hooks under staged trust
  • docs(adr): refresh Codex hook currency
  • fix(release): mark Codex installed surface explicitly
  • fix(codex): read inherited Windows hook pipes directly
  • docs(adr): record Codex release verification
  • fix(codex): avoid blocking Windows pipe reads
  • docs(adr): record final Codex wrapper reread
  • fix(release): harden Windows hook input and offline QE skip
  • fix(codex): keep held-open deadline bounded
  • docs(adr): reconcile Codex and mesh currency
  • test(codex): allow Windows shell startup budget
  • test(codex): bound held-open probe under full-suite contention
  • fix(hooks): drain held-open Windows input
  • fix(release): reject failed stranger evidence triggers

@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
explainer Ready Ready Preview Aug 6, 2026 12:16pm
ruvnet-brain Ready Ready Preview Aug 6, 2026 12:16pm

…rain no longer double-reads

Two independent fixes, both measured, neither previously on main.

RELEASE-AGGREGATE ELIGIBILITY. 051635a gated stranger-matrix one layer up, which
does not help: when that gate is false stranger concludes `skipped`, the
`workflow_run: completed` event still fires, and release-aggregate runs anyway
against a run that has no artifact. Measured on main — 5 of the last 6 runs red:

  12c29c1 -> failure   Artifact not found: stranger-evidence-12c29c12...
  051635a -> failure   (same, on the commit that supposedly fixed it)
  28fbc44 -> failure
  8f4e17c -> failure (x2)

Only a successful workflow_run or an explicit recovery dispatch is a valid
aggregate input, so the job now says exactly that. Git tags stop at v4.0.8 while
the repo reads 4.0.17-dev and npm latest is 4.0.12 — three surfaces disagreeing
about what exists is what this guard is for.

THE WIN32 DRAIN WAS A FAIL-OPEN REGRESSION. The first version of this branch's
hook-input change called onData(chunk) on what process.stdin.read() returned.
read() ITSELF emits 'data', so the registered listener already had that chunk and
every byte was counted twice. Measured, forcing platform=win32 against a real pipe:

  main            79 bytes  parses OK
  this branch v1  158 bytes PARSE-FAIL   <- payload concatenated with itself

JSON.parse threw, readStdinBounded returned an empty envelope, every gate field
read '' — so the PreToolUse wall FAILED OPEN on every Windows invocation. That is
worse than the held-open pipe it was written to fix: the original bug stalled,
this one waved commands through. CI reported it only as 30 downstream assertions
in codex-lifecycle-hooks and continuation-gate ("expected '' to contain
'ruvnet_cli_help'"), run 30858227845 — the shape of an unguarded parser defect.

Root cause is platform-independent Node stream semantics: attaching a 'readable'
listener pins the stream paused, because resume() computes
`flowing = !readableListening`. The drain is therefore the ONLY byte path on
win32, which is why the double-count was total rather than intermittent.

The drain now drains and nothing else.

GUARDED. tests/unit/hook-input.test.mjs asserts bytes === Buffer.byteLength(env)
across a process boundary with platform forced — magnitude, not direction, since
a doubling passes any non-empty check. Reintroducing the old drain fails it
`expected 158 to be 79`. Verified by doing exactly that. The branch previously
shipped this change with zero tests, which is how a 100%-reproducible defect
reached a required lane invisibly on every non-Windows dev machine.

ADR-0055 currency log records the supersession; ADR-0062 gains the guard and
governs release-aggregate.yml.

Caveat stated plainly: the double-read reproduction forces platform on darwin, so
it exercises Node's stream state machine rather than a real Windows pipe. The
ORIGINAL held-open-EOF symptom is Windows-specific and still needs a real
windows-unit run to prove this fixes it.
@stuinfla
stuinfla force-pushed the fix/4.0.8-release-process branch from e581ac7 to 9081399 Compare August 6, 2026 12:02
@stuinfla stuinfla changed the title fix/4.0.8 release process fix(ci,windows): gate release-aggregate on real evidence; win32 drain no longer double-reads Aug 6, 2026
…drain

Scope cut deliberately. This branch now carries ONE fix, not two.

RELEASE-AGGREGATE ELIGIBILITY — landing. 051635a gated stranger-matrix one layer
up, which does not help: when that gate is false stranger concludes `skipped`,
the `workflow_run: completed` event still fires, and release-aggregate runs
anyway against a run that has no artifact. Measured on main, 5 of the last 6 red:

  12c29c1 -> failure   Artifact not found: stranger-evidence-12c29c12...
  051635a -> failure   (on the very commit meant to fix it)
  28fbc44 -> failure
  8f4e17c -> failure (x2)

Only a successful workflow_run or an explicit recovery dispatch is a valid
aggregate input, so the job now says exactly that. Git tags stop at v4.0.8 while
the repo reads 4.0.17-dev and npm latest is 4.0.12 — three surfaces disagreeing
about what exists is what this guard is for.

THE WIN32 DRAIN IS WITHDRAWN — measured to fail in BOTH directions.

  attempt 1, onData on the read chunk : 158 bytes for a 79-byte envelope
                                        JSON.parse throws, gate fields read ''
                                        -> PreToolUse wall FAILS OPEN on Windows
  attempt 2, drain only               : main exits 118ms on a held-open pipe
                                        this version NEVER exits (>2000ms)
                                        -> routing-outcome-capture timedOut=true

Attempt 1 shipped in this branch and produced 30 downstream assertions in
codex-lifecycle-hooks and continuation-gate (run 30858227845). Attempt 2 was
written to fix it and traded a fail-open for a hang, caught by windows-unit job
92610172373 before merge.

Common root cause, and the reason neither is a one-liner: attaching a `readable`
listener pins stdin in paused mode permanently — resume() computes
`flowing = !readableListening` and cannot undo it — so the drain becomes the only
byte path AND the stream's exit lifecycle changes. That is a lifecycle change
masquerading as a parsing tweak.

hook-input.mjs and its test are therefore restored byte-identical to main. The
original intermittent `SessionStart: waited for stdin EOF` symptom stays OPEN and
needs a design that does not attach `readable` to process.stdin. Both
measurements are recorded in ADR-0055 so the next attempt starts from them
instead of rediscovering them.

Shipping half a fix that is proven beats shipping two where one hangs.

ADR-0062 gains the guard and now governs release-aggregate.yml.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant